//#include "gndmessage.h"

#include "gnd.h"

#include <string.h>


gboolean gnd_message_check_data(gchar * data, gint len);


GndMessage * gnd_message_new()
{
    GndMessage * message = g_new(GndMessage, 1);
    message->message_data_list = g_queue_new();
    return message;
}
GndMessage * gnd_message_new_with_data(gchar * data, gint len)
{
    if (!gnd_message_check_data(data, len)) {
        return NULL;
    }

    int offset = 8;
    gint32 data_count = *(gint32 *)(data + offset);
    data_count = g_ntohl(data_count);
    gint32 * data_len = g_new(gint32, data_count);
    offset += 4;

    int all_data_len = 0;

    for (int t = 0; t < data_count; t ++) {
        data_len[t] = *(gint32 *)(data + offset);
        data_len[t] = g_ntohl(data_len[t]);
        offset += 4;
        all_data_len += data_len[t];
    }

    if (8 + 4 + 4 * data_count + all_data_len + 8 != len) {
        return NULL;
    }

    GndMessage * message = gnd_message_new();

    GndMessageData * next_data = NULL;
    gchar * next_data_buf = NULL;
    for (int t = 0; t < data_count; t ++) {
        next_data_buf = g_new(gchar, data_len[t]);
        memcpy(next_data_buf, data + offset, data_len[t]);
        next_data = gnd_message_data_new(next_data_buf, data_len[t]);
        gnd_message_add_data(message, next_data);
        next_data = NULL;
        offset += data_len[t];
    }

    g_free(data_len);

    return message;
}


gboolean  gnd_message_free(GndMessage * message)
{
    if (message == NULL) {
        return FALSE;
    }
    g_queue_free_full(message->message_data_list, (GDestroyNotify) gnd_message_data_free);
    g_free(message);
    return TRUE;
}


gboolean  gnd_message_add_data(GndMessage * self, GndMessageData * m_data)
{
    if (!self || !m_data) {
        return FALSE;
    }

    g_queue_push_tail(self->message_data_list, (gpointer) m_data);
    return TRUE;
}

gboolean  gnd_message_add_raw_data(GndMessage * self, gchar * data, gint len)
{
    gchar * new_buf = g_memdup(data, len);
    GndMessageData * m_data = gnd_message_data_new(new_buf, len);
    //m_data->data = new_buf;
    //m_data->len = len;
    return gnd_message_add_data(self, m_data);
}

gboolean gnd_message_check_data(gchar *data, gint len)
{
    gchar start[16] = {0};
    gchar end[16] = {0};
    memcpy(start, data, 8);
    memcpy(end, data+(len-8), 8);
    return !g_strcmp0(start, end);
}


gint gnd_message_generate_bin(GndMessage *self, gchar ** out)
{
    if (!self) {
        *out = NULL;
        return 0;
    }

    int all_data_len = 0;
    int data_count = g_queue_get_length(self->message_data_list);
    GndMessageData * tmp_data = NULL;
    for (int t = 0; t < data_count; t ++) {
        tmp_data = (GndMessageData *) g_queue_peek_nth(self->message_data_list, t);
        all_data_len += tmp_data->len;
        all_data_len += 4;
    }
    all_data_len = 8 + 4 + all_data_len + 8;

    gchar * buffer = g_new(gchar, all_data_len);
    memcpy(buffer, "oxoxoxox", 8);
    memcpy(buffer + (all_data_len - 8), "oxoxoxox", 8);

    int offset = 8;
    gint32 tmp_i32 = 0;
    tmp_i32 = g_htonl(data_count);
    * (gint32 *) (buffer + offset) = tmp_i32;
    offset += 4;

    for (int t = 0; t < data_count; t ++) {
        tmp_data = (GndMessageData *) g_queue_peek_nth(self->message_data_list, t);
        tmp_i32 = g_htonl(tmp_data->len);
        * (gint32 *) (buffer + offset) = tmp_i32;
        offset += 4;
    }

    for (int t = 0; t < data_count; t ++) {
        tmp_data = (GndMessageData *) g_queue_peek_nth(self->message_data_list, t);
        memcpy(buffer + offset, tmp_data->data, tmp_data->len);
        offset += tmp_data->len;
    }

    if (offset + 8 != all_data_len) {
        g_free(buffer);
        *out = NULL;
        return 0;
    }

    *out = buffer;
    return all_data_len;
}



GndMessageData * gnd_message_data_new(gchar * data, gint len)
{
    GndMessageData * m_data = g_new(GndMessageData, 1);
    m_data->data = data;
    m_data->len = len;
    return m_data;
}

gboolean gnd_message_data_free(GndMessageData *m_data)
{
    if (m_data == NULL) {
        return FALSE;
    }

    if (m_data->data != NULL)
    {
        g_free(m_data->data);
    }

    g_free(m_data);
    return TRUE;
}


